/*
 * 获取所有com0com端口号及其所属串口对的示例代码
 * 基于com0com源码分析实现
 */

#include <windows.h>
#include <setupapi.h>
#include <stdio.h>
#include <vector>
#include <string>
#include <algorithm>

// 从com0com源码中提取的关键定义
#define C0C_BUS_DEVICE_ID           "root\\com0com"
#define C0C_PREF_PORT_NAME_A        "CNCA"
#define C0C_PREF_PORT_NAME_B        "CNCB"
#define C0C_REGSTR_VAL_PORT_NUM     "PortNum"
#define C0C_SERVICE                 "com0com"
#define C0C_PORT_NAME_COMCLASS      "COM#"

// 端口类型枚举
enum PortType {
    PORT_TYPE_UNKNOWN = 0,
    PORT_TYPE_CNC,      // CNC类端口 (CNCA0, CNCB0等)
    PORT_TYPE_COM       // COM类端口 (COM#)
};

// 串口对信息结构
struct PortPairInfo {
    int portNum;                    // 端口号 (0, 1, 2, ...)
    std::string portNameA;          // A端口名 (CNCA0, CNCA1, ...)
    std::string portNameB;          // B端口名 (CNCB0, CNCB1, ...)
    std::string realPortNameA;      // A端口实际COM名 (COM9, COM10, ...)
    std::string realPortNameB;      // B端口实际COM名 (COM11, COM12, ...)
    PortType portTypeA;             // A端口类型
    PortType portTypeB;             // B端口类型
    bool isValidA;                  // A端口是否有效配置
    bool isValidB;                  // B端口是否有效配置
};

// 判断端口名是否为COM类 (基于setup.cpp中的IsComClass函数)
bool IsComClass(const char *pPortName)
{
    if (!pPortName)
        return false;
    return _stricmp(pPortName, C0C_PORT_NAME_COMCLASS) == 0;
}

// 从设备注册表获取端口号 (基于portnum.cpp中的GetPortNum函数)
int GetPortNumFromDevice(HDEVINFO hDevInfo, PSP_DEVINFO_DATA pDevInfoData)
{
    HKEY hKey = SetupDiOpenDevRegKey(hDevInfo, pDevInfoData, 
                                     DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_READ);
    
    if (hKey == INVALID_HANDLE_VALUE)
        return -1;

    int num = -1;
    DWORD portNum;
    DWORD len = sizeof(portNum);

    if (RegQueryValueEx(hKey, C0C_REGSTR_VAL_PORT_NUM, NULL, NULL, 
                        (PBYTE)&portNum, &len) == ERROR_SUCCESS) {
        num = portNum;
    }

    RegCloseKey(hKey);
    return num;
}

// 从注册表获取端口的配置参数 (基于PortParameters类的逻辑)
bool GetPortParameters(const std::string& phPortName, std::string& portName, std::string& realPortName)
{
    char regKey[200];
    sprintf_s(regKey, sizeof(regKey), 
              "SYSTEM\\CurrentControlSet\\Services\\%s\\Parameters\\%s",
              C0C_SERVICE, phPortName.c_str());

    HKEY hKey;
    LONG err = RegOpenKeyEx(HKEY_LOCAL_MACHINE, regKey, 0, KEY_READ, &hKey);
    
    if (err != ERROR_SUCCESS)
        return false;

    // 读取PortName参数
    char portNameBuf[20] = "";
    DWORD len = sizeof(portNameBuf);
    RegQueryValueEx(hKey, "PortName", NULL, NULL, (PBYTE)portNameBuf, &len);
    
    // 读取RealPortName参数
    char realPortNameBuf[20] = "";
    len = sizeof(realPortNameBuf);
    RegQueryValueEx(hKey, "RealPortName", NULL, NULL, (PBYTE)realPortNameBuf, &len);
    
    RegCloseKey(hKey);
    
    portName = std::string(portNameBuf);
    realPortName = std::string(realPortNameBuf);
    
    return true;
}

// 确定端口类型
PortType DeterminePortType(const std::string& portName, const std::string& realPortName)
{
    if (portName.empty()) {
        return PORT_TYPE_UNKNOWN;
    }
    
    // 如果PortName是"COM#"，则为COM类端口
    if (IsComClass(portName.c_str())) {
        return PORT_TYPE_COM;
    }
    
    // 否则为CNC类端口
    return PORT_TYPE_CNC;
}

// 检查端口配置是否存在
bool IsPortConfigured(const std::string& phPortName)
{
    char regKey[200];
    sprintf_s(regKey, sizeof(regKey), 
              "SYSTEM\\CurrentControlSet\\Services\\%s\\Parameters\\%s",
              C0C_SERVICE, phPortName.c_str());

    HKEY hKey;
    LONG err = RegOpenKeyEx(HKEY_LOCAL_MACHINE, regKey, 0, KEY_READ, &hKey);
    
    if (err == ERROR_SUCCESS) {
        RegCloseKey(hKey);
        return true;
    }
    
    return false;
}

// 枚举所有com0com端口对
std::vector<PortPairInfo> EnumerateAllPortPairs()
{
    std::vector<PortPairInfo> portPairs;
    
    // 创建设备信息集，枚举所有com0com总线设备
    HDEVINFO hDevInfo = SetupDiGetClassDevs(NULL, NULL, NULL, 
                                            DIGCF_ALLCLASSES | DIGCF_PRESENT);
    
    if (hDevInfo == INVALID_HANDLE_VALUE) {
        printf("SetupDiGetClassDevs failed: %d\n", GetLastError());
        return portPairs;
    }

    SP_DEVINFO_DATA devInfoData;
    devInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
    
    // 枚举所有设备
    for (DWORD i = 0; SetupDiEnumDeviceInfo(hDevInfo, i, &devInfoData); i++) {
        char hardwareId[256];
        DWORD requiredSize;
        
        // 获取硬件ID
        if (SetupDiGetDeviceRegistryProperty(hDevInfo, &devInfoData, 
                                           SPDRP_HARDWAREID, NULL,
                                           (PBYTE)hardwareId, sizeof(hardwareId),
                                           &requiredSize)) {
            
            // 检查是否为com0com总线设备
            if (_stricmp(hardwareId, C0C_BUS_DEVICE_ID) == 0) {
                // 获取端口号
                int portNum = GetPortNumFromDevice(hDevInfo, &devInfoData);
                
                if (portNum >= 0) {
                    PortPairInfo pairInfo;
                    pairInfo.portNum = portNum;
                    
                    // 构造端口名
                    char portNameA[20], portNameB[20];
                    sprintf_s(portNameA, sizeof(portNameA), "%s%d", C0C_PREF_PORT_NAME_A, portNum);
                    sprintf_s(portNameB, sizeof(portNameB), "%s%d", C0C_PREF_PORT_NAME_B, portNum);
                    
                    pairInfo.portNameA = portNameA;
                    pairInfo.portNameB = portNameB;
                    
                    // 检查端口配置是否存在
                    pairInfo.isValidA = IsPortConfigured(pairInfo.portNameA);
                    pairInfo.isValidB = IsPortConfigured(pairInfo.portNameB);
                    
                    // 获取端口配置参数和类型
                    if (pairInfo.isValidA) {
                        std::string portName, realPortName;
                        if (GetPortParameters(pairInfo.portNameA, portName, realPortName)) {
                            pairInfo.realPortNameA = realPortName.empty() ? portName : realPortName;
                            pairInfo.portTypeA = DeterminePortType(portName, realPortName);
                        } else {
                            pairInfo.portTypeA = PORT_TYPE_UNKNOWN;
                        }
                    } else {
                        pairInfo.portTypeA = PORT_TYPE_UNKNOWN;
                    }
                    
                    if (pairInfo.isValidB) {
                        std::string portName, realPortName;
                        if (GetPortParameters(pairInfo.portNameB, portName, realPortName)) {
                            pairInfo.realPortNameB = realPortName.empty() ? portName : realPortName;
                            pairInfo.portTypeB = DeterminePortType(portName, realPortName);
                        } else {
                            pairInfo.portTypeB = PORT_TYPE_UNKNOWN;
                        }
                    } else {
                        pairInfo.portTypeB = PORT_TYPE_UNKNOWN;
                    }
                    
                    portPairs.push_back(pairInfo);
                }
            }
        }
    }
    
    SetupDiDestroyDeviceInfoList(hDevInfo);
    return portPairs;
}

// 获取端口类型字符串
const char* GetPortTypeString(PortType type)
{
    switch (type) {
        case PORT_TYPE_CNC: return "CNC";
        case PORT_TYPE_COM: return "COM";
        case PORT_TYPE_UNKNOWN: 
        default: return "Unknown";
    }
}

// 显示所有端口对信息
void DisplayPortPairs(const std::vector<PortPairInfo>& portPairs)
{
    printf("=== com0com 端口对信息 ===\n\n");
    
    if (portPairs.empty()) {
        printf("未找到任何com0com端口对。\n");
        return;
    }
    
    for (const auto& pair : portPairs) {
        printf("端口对 %d:\n", pair.portNum);
        
        // A端口信息
        printf("  %s (%s类): ", pair.portNameA.c_str(), GetPortTypeString(pair.portTypeA));
        if (pair.isValidA) {
            if (!pair.realPortNameA.empty()) {
                printf("%s\n", pair.realPortNameA.c_str());
            } else {
                printf("PortName=-\n");
            }
        } else {
            printf("未配置\n");
        }
        
        // B端口信息
        printf("  %s (%s类): ", pair.portNameB.c_str(), GetPortTypeString(pair.portTypeB));
        if (pair.isValidB) {
            if (!pair.realPortNameB.empty()) {
                printf("%s\n", pair.realPortNameB.c_str());
            } else {
                printf("PortName=-\n");
            }
        } else {
            printf("未配置\n");
        }
        
        printf("\n");
    }
    
    printf("总计: %zu 个端口对\n", portPairs.size());
}

// 主函数示例
int main()
{
    printf("正在枚举com0com端口对...\n\n");
    
    // 获取所有端口对信息
    std::vector<PortPairInfo> portPairs = EnumerateAllPortPairs();
    
    // 显示结果
    DisplayPortPairs(portPairs);
    
    // 按端口号排序显示
    std::sort(portPairs.begin(), portPairs.end(), 
              [](const PortPairInfo& a, const PortPairInfo& b) {
                  return a.portNum < b.portNum;
              });
    
    printf("\n=== 按端口号排序 ===\n");
    for (const auto& pair : portPairs) {
        printf("端口号 %d: %s (%s) <-> %s (%s)\n", 
               pair.portNum, 
               pair.portNameA.c_str(), GetPortTypeString(pair.portTypeA),
               pair.portNameB.c_str(), GetPortTypeString(pair.portTypeB));
    }
    
    // 按端口类型分类显示
    printf("\n=== 按端口类型分类 ===\n");
    
    printf("CNC类端口对:\n");
    for (const auto& pair : portPairs) {
        if (pair.portTypeA == PORT_TYPE_CNC || pair.portTypeB == PORT_TYPE_CNC) {
            printf("  端口对 %d: %s <-> %s\n", 
                   pair.portNum, pair.portNameA.c_str(), pair.portNameB.c_str());
        }
    }
    
    printf("COM类端口对:\n");
    for (const auto& pair : portPairs) {
        if (pair.portTypeA == PORT_TYPE_COM || pair.portTypeB == PORT_TYPE_COM) {
            printf("  端口对 %d: %s <-> %s\n", 
                   pair.portNum, pair.portNameA.c_str(), pair.portNameB.c_str());
        }
    }
    
    return 0;
}